﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Bluegrams.Calcex;
using System.IO;
using System.Diagnostics;

namespace Parabola
{
    public partial class MainForm : Form
    {
        Table tableview;
        string[] function = new string[5];
        Color[] graphcolor = { Color.Red, Color.Blue, Color.ForestGreen, Color.Orange, Color.Magenta };

        public MainForm()
        {
            InitializeComponent();
        }

        #region MainForm Methods

        private void MainForm_Load(object sender, EventArgs e)
        {
            this.Location = Properties.Settings.Default.Location;
            this.Size = Properties.Settings.Default.Size;
            if (Properties.Settings.Default.Maximized) this.WindowState = FormWindowState.Maximized;
            Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en");
            statAbout.Text = "v." + this.ProductVersion + statAbout.Text;
            statInfo.Text = "";
            comFunctions.Items.Add("f(x)=");
            comFunctions.Items.Add("g(x)=");
            comFunctions.Items.Add("h(x)=");
            comFunctions.Items.Add("i(x)=");
            comFunctions.Items.Add("j(x)=");
            comFunctions.SelectedIndex = 0;
            comFunctions.ForeColor = graphcolor[0];
            this.KeyPreview = true;
        }

        private void MainForm_Shown(object sender, EventArgs e)
        {
            txtFunction.Focus();
        }
        
        private void MainForm_Resize(object sender, EventArgs e)
        {
            if (WindowState == FormWindowState.Minimized)
            {
                if (tableview != null) { tableview.Hide(); }
            }
            else if (WindowState == FormWindowState.Normal)
            {
                if (tableview != null )
                {
                    tableview.Location = new Point(this.Location.X + this.Width, this.Location.Y);
                }
            }
            else if (WindowState == FormWindowState.Maximized)
            {
                if (tableview != null )
                {
                    tableview.Location = new Point(this.Location.X + 360, this.Location.Y + 30);
                }
            }
            graphbox.Width = this.Size.Width - 346;
        }

        private void MainForm_Move(object sender, EventArgs e)
        {
            if (tableview != null) tableview.Location = new Point(this.Location.X + this.Width, this.Location.Y);
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (this.WindowState == FormWindowState.Minimized)
            {
                Properties.Settings.Default.Reset();
            }
            else
            {
                Properties.Settings.Default.Location = this.Location;
                Properties.Settings.Default.Size = this.Size;
                if (this.WindowState == FormWindowState.Maximized) Properties.Settings.Default.Maximized = true;
                else Properties.Settings.Default.Maximized = false;
                Properties.Settings.Default.Save();
            }
        }

        private void conAbout_Click(object sender, EventArgs e)
        {
            InfoWindow info = new InfoWindow();
            info.ShowDialog();
        }

        #endregion

        #region Table menu

        private void butTable_Click(object sender, EventArgs e)
        {
            bool tofile = sender == conTableExport;
            if (!tofile && tableview != null) {tableview.Hide();}
            if (function[comFunctions.SelectedIndex] != null)
            {
                if (!tofile) prepare_Table();
                Dictionary<double, double> results;
                int tabstart = Convert.ToInt32(numLower.Value);
                int tabend = Convert.ToInt32(numUpper.Value);
                string checkterm = prepare_string(function[comFunctions.SelectedIndex]);
                MathParser parser = new MathParser("x");
                try
                {
                    results = parser.Parse(checkterm).EvaluateList("x", tabstart, tabend - tabstart + 1);
                    if (tofile)
                        to_File(results);
                    else to_Table(results, tabstart, tabend);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                    numLower.Focus();
                    return;
                }
                txtFunction.Focus();
                txtFunction.SelectionStart = txtFunction.TextLength;
                statInfo.Text = "Table created";
            }
            else
            {
                MessageBox.Show("No valid function to show.");
                txtFunction.Focus();
                txtFunction.SelectionStart = txtFunction.TextLength;
            }
        }

        private void prepare_Table()
        {
            int tablex, tabley;
            if (WindowState != FormWindowState.Maximized) { tablex = this.Location.X + this.Width; tabley = this.Location.Y; }
            else { tablex = this.Location.X + 360; tabley = this.Location.Y + 30; }
            tableview = new Table(tablex, tabley);
        }

        private void to_Table(Dictionary<double, double> results, int tabstart, int tabend)
        {
            tableview.listitems = results;
            tableview.start = tabstart;
            tableview.end = tabend;
            tableview.function = comFunctions.SelectedItem + " " + function[comFunctions.SelectedIndex];
            tableview.Show(this);
            tableview.Activate();
        }

        private void to_File(Dictionary<double, double> results)
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Filter = "Text File (*.txt)|.txt" + "|All Files (*.*)|*.*";
            sfd.Title = "Save Exported Data";
            sfd.FileName = "Parabola_" + comFunctions.SelectedItem.ToString().Substring(0, 4) + "_" + DateTime.Now.ToString("yyyy_mm_dd");
            sfd.FilterIndex = 0;
            string tempfile;
            if (sfd.ShowDialog() == DialogResult.OK)
                tempfile = sfd.FileName;
            else return;
            using (StreamWriter wr = new StreamWriter(tempfile))
            {
                wr.WriteLine(comFunctions.SelectedItem.ToString() + " " + function[comFunctions.SelectedIndex]);
                wr.WriteLine("--------------------");
                wr.WriteLine("{0,-8} -> {1,-8}","x","y");
                foreach (var item in results)
                {
                    if (double.IsNaN(item.Value)) wr.WriteLine("{0,-8} -> {1,-8}", item.Key, "n/a");
                    else wr.WriteLine("{0,-8} -> {1,-8:0.000}", item.Key, item.Value);
                }
            }
            Process p = new Process();
            p.StartInfo.FileName = tempfile;
            p.Start();
        }

        #endregion

        #region Function Calculation

        private string prepare_string(string term)
        {
            term = term.Replace("π", "pi");
            term = term.Replace("√", "sqrt");
            return term;
        }

        private void butResult_Click(object sender, EventArgs e)
        {
            statInfo.Text = "";
            if (txtFunction.Text != "")
            {
                if (graphbox.AddGraph(comFunctions.SelectedItem.ToString().Substring(0,1),
                    prepare_string(txtFunction.Text),graphcolor[comFunctions.SelectedIndex]))
                {
                    if (lstGraphs.SelectedIndex != -1)
                    {
                        int pos = Array.IndexOf(graphbox.Functions, prepare_string(function[comFunctions.SelectedIndex]));
                        if (pos != -1) graphbox.RemoveGraph(pos);
                    }
                    function[comFunctions.SelectedIndex] = txtFunction.Text;
                    statInfo.Text = "Successful calculation";
                    string graphitem = comFunctions.SelectedItem + " " + txtFunction.Text;
                    if (lstGraphs.SelectedIndex == -1)
                    {
                        lstGraphs.Items.Add(graphitem);
                        lstGraphs.SelectedItem = graphitem;
                    }
                    else
                    {
                        lstGraphs.Items[lstGraphs.SelectedIndex] = graphitem;
                    }
                }
                else
                {
                    txtFunction.Text = function[comFunctions.SelectedIndex];
                    statInfo.Text = "Function is not valid";
                }
            }
            txtFunction.Focus();
            txtFunction.SelectionStart = txtFunction.TextLength;
        }

        private void butNum_Click(object sender, EventArgs e)
        {
            Button sendbut = (Button)sender;
            add_chars(sendbut.Text);
        }

        private void butDEL_Click(object sender, EventArgs e)
        {
            txtFunction.Focus();
            SendKeys.Send("{BKSP}");
        }

        private void butAC_Click(object sender, EventArgs e)
        {
            txtFunction.Clear();
            txtFunction.Focus();
        }

        private void butSincos_Click(object sender, EventArgs e)
        {
            if (sender == butSin)
            {
                add_chars("sin(");
            }
            else
            {
                add_chars("cos(");
            }
        }

        private void add_chars(string chars)
        {
            int pos = txtFunction.SelectionStart;
            if (pos > 0)
            {
                string txt = txtFunction.Text.Substring(0, pos) + chars + txtFunction.Text.Substring(pos);
                txtFunction.Text = txt;
            }
            else txtFunction.Text = chars + txtFunction.Text;
            txtFunction.Focus();
            txtFunction.SelectionStart = pos + chars.Length;
            txtFunction.SelectionLength = 0;
        }

        private void txtFunction_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Return || e.KeyCode == Keys.Enter)
                butResult.PerformClick();
        }

        private void txtFunction_TextChanged(object sender, EventArgs e)
        {
            statInfo.Text = "";
        }

        #endregion

        #region Graph Selection

        private void lstGraphs_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lstGraphs.SelectedIndex != -1)
            {
                comFunctions.SelectedItem = Convert.ToString(lstGraphs.SelectedItem).Substring(0, 5);
                txtFunction.Text = function[comFunctions.SelectedIndex];
            }
        }

        private void comFunctions_SelectedIndexChanged(object sender, EventArgs e)
        {
            comFunctions.ForeColor = graphcolor[comFunctions.SelectedIndex];
            txtFunction.Text = function[comFunctions.SelectedIndex];
            lstGraphs.SelectedIndex = lstGraphs.Items.IndexOf(comFunctions.SelectedItem + " " + txtFunction.Text);
            txtFunction.Focus();
            txtFunction.SelectionStart = txtFunction.TextLength;
        }

        private void comFunctions_DropDown(object sender, EventArgs e)
        {
            comFunctions.ForeColor = SystemColors.WindowText;
        }

        private void comFunctions_DropDownClosed(object sender, EventArgs e)
        {
            comFunctions.ForeColor = graphcolor[comFunctions.SelectedIndex];
        }

        private void contxtGraphBox_Opening(object sender, CancelEventArgs e)
        {
            if (lstGraphs.SelectedIndex != -1) conDeleteBox.Enabled = true;
            else conDeleteBox.Enabled = false;
        }

        #endregion

        #region Range Settings

        private void butChangeStart_Click(object sender, EventArgs e)
        {
            graphbox.SetCenter(new Point((int)numXStart.Value, (int)numYStart.Value));
            txtFunction.Focus();
        }

        private void border_Changed(object sender, EventArgs e)
        {
            statInfo.Text = "";
            if (sender == numLower) 
            { 
                if (numUpper.Value - numLower.Value > 22)
                {
                    numUpper.Value = numLower.Value + 22;
                }
                else if (numUpper.Value - numLower.Value < 0)
                {
                    numUpper.Value = numLower.Value;
                }
            }
            else 
            { 
                if (numUpper.Value - numLower.Value > 22)
                {
                    numLower.Value = numUpper.Value - 22;
                }
                else if (numUpper.Value - numLower.Value < 0)
                {
                    numLower.Value = numUpper.Value;
                }
            }

        }

        private void groupBox4_Leave(object sender, EventArgs e)
        {
            //numXStart.Value = graphbox.Plotcenter.X;
            //numYStart.Value = graphbox.Plotcenter.Y;
        }

        #endregion

        #region Graph Edit Menu

        private void conDelete_Click(object sender, EventArgs e)
        {
            int pos = Array.IndexOf(graphbox.Functions, prepare_string(function[comFunctions.SelectedIndex]));
            if (pos == -1)
            {
                MessageBox.Show("Error: Could not delete graph.");
                return;
            }
            graphbox.RemoveGraph(pos);
            function[comFunctions.SelectedIndex] = null;
            lstGraphs.Items.Remove(lstGraphs.SelectedItem);
            if (lstGraphs.Items.Count > 0) lstGraphs.SelectedIndex = 0;
        }

        private void contxtGraphs_Opening(object sender, CancelEventArgs e)
        {
            conSelectGraph.DropDownItems.Clear();
            for (int i = 0; i < comFunctions.Items.Count; i++)
            {
                if (function[i] != null)
                {
                    ToolStripMenuItem additem = new ToolStripMenuItem(comFunctions.Items[i].ToString().Substring(0, 4));
                    if (comFunctions.SelectedItem == comFunctions.Items[i]) additem.Checked = true;
                    additem.ForeColor = graphcolor[i];
                    additem.Click += additem_Click;
                    conSelectGraph.DropDownItems.Add(additem);
                }
            }
            if (conSelectGraph.DropDownItems.Count == 0) conSelectGraph.Enabled = false;
            else conSelectGraph.Enabled = true;
            if (lstGraphs.SelectedIndex != -1) conDelete.Enabled = true;
            else conDelete.Enabled = false;
        }

        private void additem_Click(object sender, EventArgs e)
        {
            comFunctions.SelectedItem = ((ToolStripMenuItem)sender).Text + "=";
        }

        #endregion

        #region File Menu

        private void conClose_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void conClear_Click(object sender, EventArgs e)
        {
            graphbox.Clear();
            lstGraphs.Items.Clear();
            Array.Clear(function, 0, function.Length);
        }

        private void conBitmap_Click(object sender, EventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "PNG File (*.png)|*.png";
            dialog.FileName = "*.png";
            dialog.Title = "Export Graphs as Bitmap";
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                Bitmap bmp = new Bitmap(graphbox.Width, graphbox.Height);
                graphbox.DrawToBitmap(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height));
                bmp.Save(dialog.FileName, System.Drawing.Imaging.ImageFormat.Png);
                bmp.Dispose();
            }
        }

        private void conPrint_Click(object sender, EventArgs e)
        {
            Bitmap bmp = new Bitmap(graphbox.Width, graphbox.Height);
            graphbox.DrawToBitmap(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height));
            BluePrinting.PrintBmp(bmp, "Parabola Graph Plotting");
            bmp.Dispose();
        }

        #endregion
    }
}
